home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Astronomy / Moon / Source / Controller.m < prev    next >
Text File  |  1993-01-19  |  12KB  |  390 lines

  1. /* Controller.m
  2.  * Part of the Moon application for the NeXT computer.
  3.  * Author:  Geoffrey S. Knauth
  4.  * Date:    January 4, 1992
  5.  *
  6.  * Permission to copy this program is hereby granted under the terms
  7.  * of the Free Software Foundation's GNU General Public License.
  8.  */
  9.  
  10. /* Initially generated by Interface Builder */
  11.  
  12. #import <strings.h>            /* strcpy */
  13. #import <time.h>            /* struct tm, gmtime, etc. */
  14. #import <dpsclient/dpsclient.h>        /* DPSAddTimedEntry */
  15. #import <appkit/Application.h>        /* NX_BASETHRESHOLD */
  16. #import <appkit/Font.h>            /* NX_IDENTITYMATRIX */
  17. #import <appkit/publicWraps.h>        /* NXBeep */
  18. #import <objc/NXStringTable.h>
  19. #import "all.h"
  20. #import "Controller.h"
  21. #import "MoonView.h"
  22. #import "MoonIconView.h"
  23.  
  24. static DPSTimedEntry te = 0;
  25.  
  26. void tick(DPSTimedEntry teNumber, double now, void *userData);
  27.  
  28. @implementation Controller
  29.  
  30. - appDidHide :sender
  31. {
  32.     [iconView display];
  33.     return self;
  34. }
  35.  
  36. - appDidInit:sender
  37. {
  38.     NXRect iconRect = {{0.0, 0.0}, {64.0, 64.0}};
  39.     long t;            /* seconds since 0000 UTC 1/1/1970 */
  40.     struct tm *tmLcl;
  41.     char buf[80];
  42.  
  43.     nextNewMoon = 0.0;        /* invalidate new moon info */
  44.  
  45.   /* make our icon view be the appIcon window's contentView */
  46.     iconView = [[MoonIconView alloc] initFrame:&iconRect];
  47.     [[[NXApp appIcon] setContentView:iconView] free];
  48.     
  49.   /* The nowButton should not be enabled until the user has gone into
  50.    * time travel mode.
  51.    */
  52.     [nowButton setEnabled:NO];
  53.  
  54.     [statForm setTextFont:
  55.      [Font newFont:"Ohlfs" size:10.0 matrix:NX_FLIPPEDMATRIX]];
  56.  
  57.   /* The local time title must be big enough to hold "Local (GMT+11:30)". */
  58.     (void) time(&t);
  59.     tmLcl = localtime(&t);
  60.     sprintf(buf, "Local (%s)", tmLcl->tm_zone);
  61.     [statForm setTitle:buf at:iLocalTime];
  62.  
  63.   /* Make sure the function tick (which sends us an update message)
  64.    * is called on a regular basis.
  65.    */
  66.     te = DPSAddTimedEntry(TICK_SECONDS, tick, self, NX_BASETHRESHOLD);
  67.  
  68.     [self update];    /* update now, in case the wait is long */
  69.  
  70.     return self;
  71. }
  72.  
  73. - appWillTerminate :sender
  74. {
  75.     DPSRemoveTimedEntry(te);
  76.     return self;
  77. }
  78.  
  79. - getUserGmtTime :(int *)year :(int *)month :(int *)day
  80.          :(int *)hour :(int *)minute :(int *)second
  81. {
  82.     char buf[80], monthStr[20];
  83.     int i, n, expected, hhmm;
  84.  
  85.   /* Here are the kinds of input we can accept:
  86.    * hh:mm:ss dd mmmmm yyyy    %d:%d:%d %d %s %d    (6)
  87.    * hh:mm dd mmmmm yyyy    %d:%d %d %s %d        (5)
  88.    * hhmm dd mmmmm yyyy        %d %d %s %d        (4)
  89.    * dd mmmmm yyyy        %d %s %d        (3)
  90.    * mmmmm yyyy            %s %d            (2)
  91.    * yyyy            %d            (1)
  92.    */
  93.     strcpy(buf, [travelText stringValue]);
  94.     for (i = n = 0; i < 6; n = 0, i++) {
  95.     expected = *hour = *minute = *second = *day = *month = *year = 0;
  96.     switch (i) {
  97.       case 0:
  98.         expected = 6;
  99.         n = sscanf(buf, "%d:%d:%d %d %s %d",
  100.                hour, minute, second, day, monthStr, year);
  101.         break;
  102.       case 1:
  103.         expected = 5;
  104.         n = sscanf(buf, "%d:%d %d %s %d",
  105.                hour, minute, day, monthStr, year);
  106.         break;
  107.       case 2:
  108.         expected = 4;
  109.         n = sscanf(buf, "%d %d %s %d", &hhmm, day, monthStr, year);
  110.         break;
  111.       case 3:
  112.         expected = 3;
  113.         n = sscanf(buf, "%d %s %d", day, monthStr, year);
  114.         break;
  115.       case 4:
  116.         expected = 2;
  117.         n = sscanf(buf, "%s %d", monthStr, year);
  118.         *day = 1;
  119.         break;
  120.       case 5:
  121.         expected = 1;
  122.         n = sscanf(buf, "%d", year);
  123.         *day = 1;
  124.         strcpy(monthStr, [stringTable valueForStringKey:"1"]);
  125.         break;
  126.     }
  127.     if (n && n == expected) break;
  128.     }
  129.     if (n) {
  130.     *month = [self monthNumFromStr:monthStr];
  131.     if (*month == 0) goto bad;
  132.     if (expected == 4) {
  133.         *hour   = hhmm / 100;
  134.         *minute = hhmm % 100;
  135.     }
  136.     return self;
  137.     }
  138. bad:
  139.     *hour = *minute = *second = *day = *month = *year = 0;
  140.     return nil;
  141. }
  142.  
  143. - (int)monthNumFromStr :(const char *)month
  144. {
  145.     char monthBuf[2+1];
  146.     int i;
  147.  
  148.     for (i = 12; i > 0; i--) {
  149.     sprintf(monthBuf, "%d", i);
  150.     if (strcmp(month, [stringTable valueForStringKey:monthBuf]) == 0)
  151.         break;
  152.     }
  153.     return i;
  154. }
  155.  
  156. - pause :sender
  157. {
  158.   /* We need a pause method, so that when we are coming out of pause,
  159.    * resuming to run, we can generate an update right away, without
  160.    * making the user wait for the next official tick.
  161.    */
  162.     if ([sender state] == RUNNING)    /* state changed before action sent */
  163.     [self update];
  164.  
  165.     return self;
  166. }
  167.  
  168. - returnToNow :sender
  169. {
  170.   /* When the nowButton is disabled, we are not in time travel mode.
  171.    * When are not in time travel mode, update gets its time from the system,
  172.    * not from the user.
  173.    */
  174.     [sender setEnabled:NO];        /* sender is also nowButton */
  175.     [pauseButton setEnabled:YES];    /* by definition we will run */
  176.     nextNewMoon = 0.0;            /* invalidate new moon info */
  177.     [self update];
  178.     return self;
  179. }
  180.  
  181. - timeTravel :sender
  182. {
  183.   /* When the nowButton is enabled, we are in time travel mode.
  184.    * When are in time travel mode, update gets its time from the user,
  185.    * not from the system.
  186.    */
  187.     [nowButton setEnabled:YES];
  188.     [pauseButton setEnabled:NO];    /* by definition we will pause */
  189.     nextNewMoon = 0.0;            /* invalidate new moon info */
  190.     [self update];
  191.     return self;
  192. }
  193.  
  194. - update
  195. {
  196.     long t;                /* seconds since 0000 UTC 1/1/1970 */
  197.     static long gmtoff = -1L;        /* timezone offset from UTC in secs */
  198.     struct tm *tmUtc, *tmLcl;        /* Unix UTC & local time structures */
  199.     float p;                /* moon phase: 0=new, 0.5=full */
  200.     int lunation;
  201.     double jd, aom, cphase, cdist, cangdia, csund, csuang, lptime;
  202.     double phasar[5];
  203.     double fakeJd;            /* don't use for astro calculations! */
  204.     char tbuf[80];
  205.     char monthBuf[2+1];            /* "1" .. "12" */
  206.     int yy, mm, dd, hh, mmm, ss;    /* moon times */
  207.     int year, month, day;        /* our UTC times */
  208.     int hour, minute, second;        /* our UTC times */
  209.     int lyear, lmonth, lday;        /* our local times */
  210.     int lhour, lminute, lsecond;    /* our local times */
  211.  
  212.     if ([pauseButton state] == PAUSED) return nil;
  213.  
  214.     if ([nowButton isEnabled]) {
  215.       /* We're traveling in time.
  216.        * This is not the initial state, so gmtoff is legitimate.
  217.        */
  218.     if ([self getUserGmtTime
  219.          :&year :&month :&day :&hour :&minute :&second] == nil)
  220.     {
  221.         NXBeep();
  222.         [nowButton setEnabled:NO];    /* no time travel after all */
  223.         return nil;
  224.     }
  225.     jd = ymdhmsToJtime(year, month, day, hour, minute, second);
  226.     fakeJd = jd + (double)gmtoff / 86400.;
  227.     } else {
  228.       /* We're not traveling in time.  Unix gives us our time info.
  229.        * This is the initial state.
  230.        */
  231.     (void) time(&t);
  232.     tmUtc = gmtime(&t);
  233.     jd = jtime(tmUtc);
  234.     year   = tmUtc->tm_year + 1900;
  235.     month  = tmUtc->tm_mon + 1;
  236.     day    = tmUtc->tm_mday;
  237.     hour   = tmUtc->tm_hour;
  238.     minute = tmUtc->tm_min;
  239.     second = tmUtc->tm_sec;
  240.     tmLcl = localtime(&t);
  241.     fakeJd = jtime(tmLcl);
  242.     if (gmtoff == -1L) {
  243.       /* Save the time zone offset for later.  We'll need it if the user
  244.        * does any time traveling, because if we go too far into the past
  245.        * or future, Unix time functions won't help us any more.
  246.        */
  247.         gmtoff = tmLcl->tm_gmtoff;    /* remember offset is in seconds */
  248.     }
  249.     }
  250.     p = (float)jdtophase(jd, &cphase, &aom, &cdist, &cangdia, &csund, &csuang);
  251.  
  252.   /* Draw the big image of the moon and shadow it. */
  253.     [moonView setPhase:p];
  254.     [moonView display];
  255.  
  256.   /* Draw the little icon image of the moon and shadow it. */
  257.     [iconView setPhase:p];
  258.     [iconView display];
  259.  
  260.   /* Update textual information */
  261.     
  262.     sprintf(tbuf, "%.5f", jd);    /* 1979 January 1.0 <==> JD 2443874.5 */
  263.     [statForm setStringValue:tbuf at:iJulianDate];
  264.  
  265.     sprintf(monthBuf, "%d", month);
  266.     sprintf(tbuf, "%02d:%02d:%02d %d %s %d",
  267.         hour, minute, second, day,
  268.         [stringTable valueForStringKey:monthBuf], year);
  269.     [statForm setStringValue:tbuf at:iUniversalTime];
  270.  
  271.     jyear(fakeJd, &lyear, &lmonth, &lday);
  272.     jhms(fakeJd, &lhour, &lminute, &lsecond);
  273.     sprintf(monthBuf, "%d", lmonth);
  274.     sprintf(tbuf, "%02d:%02d:%02d %d %s %d",
  275.         lhour, lminute, lsecond, lday,
  276.         [stringTable valueForStringKey:monthBuf], lyear);
  277.     [statForm setStringValue:tbuf at:iLocalTime];
  278.  
  279.     sprintf(tbuf, "%d%% %s, %s",
  280.         (int) rint(cphase * 100),
  281.         [stringTable valueForStringKey:"visible"],
  282.         p < 0.5 ?
  283.         [stringTable valueForStringKey:"waxing"]
  284.         : [stringTable valueForStringKey:"waning"]);
  285.     [statForm setStringValue:tbuf at:iMoonPhase];
  286.  
  287.   /* Some information about the Moon */
  288.  
  289.     sprintf(tbuf, "%dd %dh %dm",
  290.         (int) aom, (int) (24 * (aom - floor(aom))),
  291.         ((int) (1440 * (aom - floor(aom)))) % 60);
  292.     [statForm setStringValue:tbuf at:iAgeOfMoon];
  293.  
  294.     sprintf(tbuf, "%ld km, %.1f %s",
  295.         (long) cdist, cdist / earthrad,
  296.         [stringTable valueForStringKey:"earthRadii"]);
  297.     [statForm setStringValue:tbuf at:iMoonDistance];
  298.  
  299.     sprintf(tbuf, "%.4f %s", cangdia,
  300.         [stringTable valueForStringKey:"degrees"]);
  301.     [statForm setStringValue:tbuf at:iMoonSubtends];
  302.  
  303.   /* Information about the Sun */
  304.     
  305.     sprintf(tbuf, "%.0f km, %.3f AU", csund, csund / sunsmax);
  306.     [statForm setStringValue:tbuf at:iSunDistance];
  307.  
  308.     sprintf(tbuf, "%.4f %s", csuang,
  309.         [stringTable valueForStringKey:"degrees"]);
  310.     [statForm setStringValue:tbuf at:iSunSubtends];
  311.  
  312.   /* Calculate times of phases of this lunation.  This is sufficiently
  313.    * time-consuming that we only do it once a month, or when we begin or
  314.    * return from time travel.  If we've just changed the time travel mode,
  315.    * nextNewMoon will be zero.
  316.    */
  317.     if (jd > nextNewMoon) {
  318.     phasehunt(jd, phasar);
  319.     lptime = phasar[0];
  320.     lunation = floor(((lptime + 7) - lunatbase) / synmonth) + 1;
  321.     jyear(lptime, &yy, &mm, &dd);
  322.     jhms(lptime, &hh, &mmm, &ss);
  323.     sprintf(monthBuf, "%d", mm);
  324.     sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
  325.         [stringTable valueForStringKey:monthBuf], yy);
  326.     [statForm setStringValue:tbuf at:iLastNewMoon];
  327.  
  328.     sprintf(tbuf, "%d", lunation);
  329.     [statForm setStringValue:tbuf at:iThisLunation];
  330.  
  331.     lptime = phasar[1];
  332.     jyear(lptime, &yy, &mm, &dd);
  333.     jhms(lptime, &hh, &mmm, &ss);
  334.     sprintf(monthBuf, "%d", mm);
  335.     sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
  336.         [stringTable valueForStringKey:monthBuf], yy);
  337.     [statForm setStringValue:tbuf at:iFirstQuarter];
  338.  
  339.     lptime = phasar[2];
  340.     jyear(lptime, &yy, &mm, &dd);
  341.     jhms(lptime, &hh, &mmm, &ss);
  342.     sprintf(monthBuf, "%d", mm);
  343.     sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
  344.         [stringTable valueForStringKey:monthBuf], yy);
  345.     [statForm setStringValue:tbuf at:iFullMoon];
  346.  
  347.     lptime = phasar[3];
  348.     jyear(lptime, &yy, &mm, &dd);
  349.     jhms(lptime, &hh, &mmm, &ss);
  350.     sprintf(monthBuf, "%d", mm);
  351.     sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
  352.         [stringTable valueForStringKey:monthBuf], yy);
  353.     [statForm setStringValue:tbuf at:iLastQuarter];
  354.  
  355.     nextNewMoon = phasar[4];
  356.     jyear(nextNewMoon, &yy, &mm, &dd);
  357.     jhms(nextNewMoon, &hh, &mmm, &ss);
  358.     sprintf(monthBuf, "%d", mm);
  359.     sprintf(tbuf, "%02d:%02d UTC %d %s %d", hh, mmm, dd,
  360.         [stringTable valueForStringKey:monthBuf], yy);
  361.     [statForm setStringValue:tbuf at:iNextNewMoon];
  362.  
  363.     sprintf(tbuf, "%d", lunation + 1);
  364.     [statForm setStringValue:tbuf at:iNextLunation];
  365.     }
  366.     return self;
  367. }
  368.  
  369. void tick(DPSTimedEntry teNumber, double now, void *userData)
  370. {
  371.     Controller *self = userData;    /* this trick lets C do some Obj-C */
  372.  
  373.   /* We only came here because of the interface from the DPSTimedEntry.
  374.    * Now let's be more objective.
  375.    */
  376.     if ([self->nowButton isEnabled]) {
  377.       /* We're in time travel mode.
  378.        * That means only the user should be able to generate update events.
  379.        * While in this mode, ticks will be ignored.
  380.        * We do this so that the user may finish typing a time travel date
  381.        * and tell us when to read it, otherwise we are likely to read it
  382.        * prematurely.
  383.        */
  384.     return;
  385.     }
  386.     [self update];
  387. }
  388.  
  389. @end
  390.